﻿#ifdef WIN32

#include "Async/Process.h"
#include "Output/Console.h"
#include "Path/Paths.h"
#include "Async/PackagedTask.h"

#include "Platform/WindowsPlatform.h"

#define UNICODE
#include <Windows.h>


class CProcessPlatformData
{
public:
    //进程句柄
    HANDLE ProcessHandle=nullptr;
    HANDLE ThreadHandle=nullptr;
    DWORD ProcessId=0;
    DWORD ThreadId=0;

    //标准输出管道
    HANDLE StdoutRead=nullptr;
    HANDLE StdoutWrite=nullptr;

    //标准错误管道
    HANDLE StderrRead=nullptr;
    HANDLE StderrWrite=nullptr;
};

void RProcess::CreatePlatformData()
{
    PlatformData=new CProcessPlatformData();
}

void RProcess::DestroyPlatformData()
{
    delete PlatformData;
    PlatformData=nullptr;
}


void RProcess::Start(
    const CString &Program
    , const TVector<CString> &Arguments
    ,bool bReciveOutput
    ,bool bReciveError
    )
{
    assert(State==EProcessState::NotRunning);
    State=EProcessState::Starting;

    //创建管道以读取子进程的输出
    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = nullptr;

    HANDLE hChildStdoutRd, hChildStdoutWr;
    if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
    {
        WH::PrintLine(U"启动进程失败: 创建通信管道失败");
        return;
    }

    //创建错误管道
    HANDLE hChildStderrRd, hChildStderrWr;
    if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
    {
        WH::PrintLine(U"启动进程失败: 创建通信管道失败");

        CloseHandle(hChildStdoutRd);
        CloseHandle(hChildStdoutWr);
        return;
    }


    CString CommandLine=Program;
    for(const auto& Arg : Arguments)
    {
        CommandLine+=U" ";
        CommandLine+=Arg;
    } 
    auto CommandLineUTF16=CommandLine.ToUtf16();
    auto WorkingDirectoryUTF16=GetWorkingDirectory().ToString().ToUtf16();

    PROCESS_INFORMATION ProcessInfo;
    ZeroMemory(&ProcessInfo,sizeof(ProcessInfo));
    STARTUPINFO StartupInfo;
    ZeroMemory(&StartupInfo,sizeof(StartupInfo));
    StartupInfo.cb=sizeof(StartupInfo);
    StartupInfo.dwFlags |=STARTF_USESTDHANDLES;
    StartupInfo.hStdOutput=hChildStdoutWr;
    StartupInfo.hStdError=hChildStderrWr;
    //StartupInfo.hStdInput=hChildStdinWr;

    BOOL Success=CreateProcess(
        nullptr
        ,(LPWSTR)CommandLineUTF16.Data()
        ,nullptr
        ,nullptr
        ,TRUE
        ,0
        ,nullptr
        ,(LPWSTR)WorkingDirectoryUTF16.Data()
        ,&StartupInfo
        ,&ProcessInfo
    );

    if(!Success)
    {
        State=EProcessState::NotRunning;

        //输出错误信息
        DWORD ErrorCode=GetLastError();
        LPVOID lpMsgBuf;
        FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
            ,nullptr
            ,ErrorCode
            ,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
            ,(LPTSTR)&lpMsgBuf
            ,0
            ,nullptr
        );

        //将Ansi编码转换为utf32编码
        auto ErrorMessage=CWindowsPlatform::Get()->SystemStringToUnicode((const char*)lpMsgBuf);
        WH::PrintLine(U"启动进程失败: {}",ErrorMessage);

        //关闭管道
        CloseHandle(hChildStdoutRd);
        CloseHandle(hChildStdoutWr);

        CloseHandle(hChildStderrRd);
        CloseHandle(hChildStderrWr);
        
        return;
    }

    PlatformData->ProcessHandle=ProcessInfo.hProcess;
    PlatformData->ThreadHandle=ProcessInfo.hThread;
    PlatformData->ProcessId=ProcessInfo.dwProcessId;
    PlatformData->ThreadId=ProcessInfo.dwThreadId;

    PlatformData->StdoutRead=hChildStdoutRd;
    PlatformData->StdoutWrite=hChildStdoutWr;

    PlatformData->StderrRead=hChildStderrRd;
    PlatformData->StderrWrite=hChildStderrWr;
    
    State=EProcessState::Running;

    if(bReciveOutput)
    {
        StartReciveOutputTask();
    }

    if(bReciveError)
    {
        StartReciveErrorTask();
    }

    StartDetecteTask();

}

void RProcess::Stop()
{
    //关闭进程

    if(State==EProcessState::Running)
    {
        TerminateProcess(PlatformData->ProcessHandle,0);
        CloseHandle(PlatformData->ProcessHandle);
        CloseHandle(PlatformData->ThreadHandle);
        PlatformData->ProcessHandle=nullptr;
        PlatformData->ThreadHandle=nullptr;
        PlatformData->ProcessId=0;
        PlatformData->ThreadId=0;
        State=EProcessState::NotRunning;
    }
}

void RProcess::WaitForFinished(int64_t Ms)
{
    if(State==EProcessState::Running)
    {
        WaitForSingleObject(PlatformData->ProcessHandle,Ms);
    }
}




void RProcess::StartReciveOutputTask()
{

    //启动一个异步任务，读取子进程的输出
    auto OutRead=PlatformData->StdoutRead;
    std::shared_ptr<CPackagedTask<TVector<uint8_t>>> 
            OutputReadTask = WH::CreateAsyncTask<TVector<uint8_t>>(
                [OutRead](bool& )mutable ->TVector<uint8_t>
    {
        //从管道中读取子进程的输出
        DWORD BytesRead;
        uint8_t Buffer[4096];
        if (!ReadFile(OutRead, Buffer, sizeof(Buffer), &BytesRead, nullptr) || BytesRead == 0)
        {
            //什么都没有读取到
            return TVector<uint8_t>();
        }
        TVector<uint8_t> Output(Buffer, BytesRead);
        return Output;
    });

    RProcessWeakPtr WeakThis=this;
    OutputReadTask->ThenInMainThread<void>(
        [OutputReadTask,WeakThis](bool& bSucceed)mutable ->void
    {
        if(!WeakThis)
        {
            //进程已经被销毁，不需要再输出
            CloseHandle(WeakThis->PlatformData->StdoutRead);
            CloseHandle(WeakThis->PlatformData->StdoutWrite);

            WeakThis->PlatformData->StdoutRead=nullptr;
            WeakThis->PlatformData->StdoutWrite=nullptr;
            return;
        }

        WeakThis->NotifyStandardOutput(
            std::move(OutputReadTask->Task->GetResult())
        );
        
        //继续读取
        WeakThis->StartReciveOutputTask();
    });
    OutputReadTask->Start();
}

void RProcess::StartReciveErrorTask()
{
    //启动一个异步任务，读取子进程的输出
    auto OutRead=PlatformData->StderrRead;
    std::shared_ptr<CPackagedTask<TVector<uint8_t>>> 
            OutputReadTask = WH::CreateAsyncTask<TVector<uint8_t>>(
                [OutRead](bool& )mutable ->TVector<uint8_t>
    {
        //从管道中读取子进程的输出
        DWORD BytesRead;
        uint8_t Buffer[4096];
        BOOL Succeed=ReadFile(OutRead, Buffer, sizeof(Buffer), &BytesRead, nullptr);
        if (!Succeed || BytesRead == 0)
        {
            //什么都没有读取到
            return TVector<uint8_t>();
        }
        TVector<uint8_t> Output(Buffer, BytesRead);
        return Output;
    });

    RProcessWeakPtr WeakThis=this;
    OutputReadTask->ThenInMainThread<void>(
        [OutputReadTask,WeakThis](bool& bSucceed)mutable ->void
    {
        if(!WeakThis)
        {
            //进程已经被销毁，不需要再输出
            CloseHandle(WeakThis->PlatformData->StdoutRead);
            CloseHandle(WeakThis->PlatformData->StdoutWrite);

            WeakThis->PlatformData->StdoutRead=nullptr;
            WeakThis->PlatformData->StdoutWrite=nullptr;
            return;
        }

        WeakThis->NotifyStandardError(
            std::move(OutputReadTask->Task->GetResult())
        );
        
        //继续读取
        WeakThis->StartReciveErrorTask();
    });
    OutputReadTask->Start();
}

//异步检测进程是否结束
void RProcess::StartDetecteTask()
{
    auto _ProcessHandle=PlatformData->ProcessHandle;
    std::shared_ptr<CPackagedTask<DWORD>> 
            DetecteTask = WH::CreateAsyncTask<DWORD>(
                [_ProcessHandle](bool& )mutable ->DWORD
    {
        DWORD ExitCode;
        GetExitCodeProcess(_ProcessHandle, &ExitCode);
        return ExitCode;
    });

    RProcessWeakPtr WeakThis=this;
    DetecteTask->ThenInMainThread<void>(
        [DetecteTask,WeakThis](bool& bSucceed)mutable ->void
    {
        if(!WeakThis)
        {
            //进程已经被销毁，不需要再检测
            return;
        }

        DWORD ExitCode=DetecteTask->Task->GetResult();
        if(ExitCode!=STILL_ACTIVE)
        {
            //进程已经结束
            WeakThis->State=EProcessState::NotRunning;
            WeakThis->OnFinished.Broadcast(ExitCode,ExitCode==0);

            return;
        }
        
        //继续检测
        WeakThis->StartDetecteTask();
    });
    DetecteTask->Start();
}

uint64_t RProcess::GetProcessId() const
{
    return PlatformData->ProcessId;
}


#endif